package com.example.java_gobang.api;

import com.example.java_gobang.game.*;
import com.example.java_gobang.mapper.UserMapper;
import com.example.java_gobang.model.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

@Component
@Slf4j
public class GameAPI extends TextWebSocketHandler {

    private ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private RoomManager roomManager;

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Resource
    private UserMapper userMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse resp = new GameReadyResponse();
        //1.先获取到用户的身份信息，从httpsession中拿到用户的对象
        User user = (User)session.getAttributes().get("user");
        if(user == null){
            resp.setOk(false);
            resp.setReason("用户尚未登录");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            session.close();
            return;
        }
        // 2.判定当前用户是否已经进入房间，拿着房间管理器进行查询
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room == null){
            //如果为空表示当前没有对应房间，即该玩家没有匹配到
            resp.setOk(false);
            resp.setMessage("用户未匹配到");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        //3.判定当前是不是多开
        if (onlineUserManager.getFromGameRoom(user.getUserId()) != null || onlineUserManager.getFromGameHall(user.getUserId()) != null) {
            resp.setOk(false);
            resp.setMessage("禁止多开游戏页面");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
            return;
        }
        //4.设置当前玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);

        //这里使用房间对象进行加锁，是因为不同房间的玩家之间是不互斥的
        synchronized (room){
            //5.把两个玩家加入到游戏房间中
            //若这个是第一个玩家
            if(room.getUser1() == null){
                room.setUser1(user);
                //将先连到房间的玩家作为先手方
                room.setWhiteUser(user.getUserId());
                log.info("玩家" + user.getUsername() + "准备就绪");
                return;
            }
            if(room.getUser2() == null){
                room.setUser2(user);
                log.info("玩家" + user.getUsername() + "准备就绪");
                //通知前端游戏双方都已经准备好了
                noticeGameReady(room);
                return;
            }
        }
        //此处如果又有玩家连接到同一个房间，为了提高健壮性而写
        resp.setOk(false);
        resp.setReason("当前房间已满");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp)));
    }

    private void noticeGameReady(Room room) throws IOException {
        WebSocketSession session1 = onlineUserManager.getFromGameRoom(room.getUser1().getUserId());
        WebSocketSession session2 = onlineUserManager.getFromGameRoom(room.getUser2().getUserId());
        //通知玩家1
        GameReadyResponse resp1 = new GameReadyResponse();
        resp1.setOk(true);
        resp1.setMessage("gameReady");
        resp1.setReason("");
        resp1.setRoomId(room.getRoomId());
        resp1.setThisUserId(room.getUser1().getUserId());
        resp1.setThatUserId(room.getUser2().getUserId());
        resp1.setWhiteUser(room.getWhiteUser());
        session1.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp1)));

        //通知玩家2
        GameReadyResponse resp2 = new GameReadyResponse();
        resp2.setOk(true);
        resp2.setMessage("gameReady");
        resp2.setReason("");
        resp2.setRoomId(room.getRoomId());
        resp2.setThisUserId(room.getUser2().getUserId());
        resp2.setThatUserId(room.getUser1().getUserId());
        resp2.setWhiteUser(room.getWhiteUser());
        session2.sendMessage(new TextMessage(objectMapper.writeValueAsString(resp2)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //1.先从session中获取到用户的身份信息
        User user = (User)session.getAttributes().get("user");
        if(user == null){
            log.info("[handleTextMessage]当前玩家尚未登录");
            return;
        }
        //2.根据玩家id获取到房间对象
        Room room = roomManager.getRoomByUserId(user.getUserId());
        //3.通过room对象来处理这次具体的请求
        room.putChess(message.getPayload());
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User)session.getAttributes().get("user");
        if(user == null){
            //此处就简单处理，在断开链接的时候就不给客户端返回响应了
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if(session == exitSession){
            onlineUserManager.existGameRoom(user.getUserId());
        }
        log.info("用户" + user.getUsername() + "异常退出");
        noticeThatUserWin(user);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User)session.getAttributes().get("user");
        if(user == null){
            //此处就简单处理，在断开链接的时候就不给客户端返回响应了
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if(session == exitSession){
            onlineUserManager.existGameRoom(user.getUserId());
        }
        log.info("用户" + user.getUsername() + "离开游戏房间");
        noticeThatUserWin(user);
    }

    private void noticeThatUserWin(User user) throws IOException {
        //1.根据当前玩家找到房间
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if(room == null){
            // 该房间已经释放了
            return;
        }
        //2.根据房间找到对手
        User thatUser = user == room.getUser1() ? room.getUser2() : room.getUser1();
        WebSocketSession session = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if(session == null){
            //对手也掉线了
            return;
        }
        GameResponse response = new GameResponse();
        response.setUserId(thatUser.getUserId());
        response.setWinner(thatUser.getUserId());
        response.setMessage("putChess");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
        //释放房间对象
        roomManager.remove(room.getRoomId());
        //更新玩家分数信息
        userMapper.userWin(thatUser.getUserId());
        userMapper.userLose(user.getUserId());
    }
}
